// A dynamic route — `/blog/` — pre-rendered STATICALLY, one HTML // file per post. Three exports make that work: // // • getStaticPaths → enumerate which slugs to build (from the content // source; point it at a CMS/DB in a real app). // • loader → fetch THIS post's data; runs at build per slug. // • meta(loaderData)→ per-post /<description> baked into the HTML. // // `interactive: 'islands'` hydrates ONLY the <ReadingProgress> island; the // article itself is inert HTML. import type { ReactNode } from 'react' import { useLoaderData, notFound, type LoaderFn, type PageMeta } from '@voltro/web' import { T, useLocale } from '@voltro/i18n' import { posts, type Post } from '../../../content/posts' import { withLocalePrefix } from '../../../lib/locale' import ReadingProgress from '../../../components/ReadingProgress.island' export const renderMode = 'static' as const export const interactive = 'islands' as const // Which concrete paths to pre-render. Every returned `params` becomes one // built HTML file; un-enumerated slugs are simply not built (→ 404). export const getStaticPaths = async (): Promise<Array<{ params: { slug: string } }>> => posts.map((post) => ({ params: { slug: post.slug } })) // Runs at build time for each enumerated slug. Returning `notFound()` skips // the artifact at build / 404s at runtime — defensive, though getStaticPaths // only ever feeds us slugs that exist. export const loader: LoaderFn<Post> = async ({ params }) => { const post = posts.find((p) => p.slug === params.slug) if (!post) return notFound(`post ${params.slug}`) return post } // `meta` as a function of the loader data → correct per-post <title> in the // pre-rendered HTML (great for SEO + social cards). The title/description are // POST CONTENT, not chrome, so they are the same on `/blog/x` and `/de/blog/x` // — no per-locale variant needed here. export const meta = ({ loaderData }: { loaderData: Post }): PageMeta => ({ title: `${loaderData.title} — {{capProjectName}}`, description: loaderData.excerpt, }) export default function BlogPost(): ReactNode { const post = useLoaderData<Post>() // The active locale (from the layout's URL provider) keeps the "all posts" // link on the current language. const locale = useLocale() return ( <main> <ReadingProgress /> <p> <a href={withLocalePrefix('/', locale)}> <T id="post.backToList" /> </a> </p> <article> <h1>{post.title}</h1> <p className="muted"> {post.date} · <T id="post.readingTimeRead" values={{ minutes: post.readingMinutes }} /> </p> {post.body.split('\n\n').map((para, i) => ( <p key={i}>{para}</p> ))} </article> </main> ) }